﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using Microsoft.WindowsAzure;
using AzureDriveCore;

namespace AzureDriveUI
{
	public static class DriveConfigurationManager
	{
		static DriveConfigurationManager()
		{
			_configFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
				@"AzureDriveUI\config.xml");
		}

		private static string _configFilePath;
		private static List<DriveConfiguration> _configuredDrives;

		public static IEnumerable<DriveConfiguration> GetConfiguredDrives()
		{
			foreach (var item in _configuredDrives)
			{
				yield return item;
			}
		}

		public static void LoadConfiguration()
		{
			List<DriveConfiguration> configuredDrives = new List<DriveConfiguration>();

			if (File.Exists(_configFilePath) == false)
			{
				string directory = Path.GetDirectoryName(_configFilePath);

				if (Directory.Exists(directory) == false)
				{
					Directory.CreateDirectory(directory);
				}
			}

			if (File.Exists(_configFilePath))
			{
				try
				{
					var configDocument = XDocument.Load(_configFilePath);

					foreach (var item in configDocument.Root.Elements("drive"))
					{
						DriveConfiguration driveConfiguration = null;

						try
						{
							driveConfiguration = new DriveConfiguration
							{
								DriveLetter = item.Attribute("driveLetter").Value,
								ConnectionString = item.Attribute("connectionString").Value,
								VolumeLabel = item.Attribute("volumeLabel").Value,
							};
						}
						catch
						{
							throw new ArgumentException("Configuration node must have valid 'driveLetter', 'connectionString' and 'volumeLabel' attributes.");
						}

						if (Regex.IsMatch(driveConfiguration.DriveLetter, "^[a-zA-Z]$") == false)
						{
							throw new ArgumentException("Invalid drive-letter specified in the config file.");
						}

						if (driveConfiguration.VolumeLabel != null && driveConfiguration.VolumeLabel.Length > 32)
						{
							throw new ArgumentException("Invalid volumeLabel specified in the config file. VolumeLabel cannot exceed 32 characters in length.");
						}

						try
						{
							CloudStorageAccount.Parse(driveConfiguration.ConnectionString);
						}
						catch
						{
							throw new ArgumentException(string.Format("Invalid connection string specified in the config file for drive {0}.", driveConfiguration.DriveLetter));
						}

						configuredDrives.Add(driveConfiguration);
					}

				}
				catch { throw; }
			}

			_configuredDrives = configuredDrives;
		}

		public static void RemoveDriveConfiguration(string driveLetter)
		{
			if (Regex.IsMatch(driveLetter, "^[a-zA-Z]$") == false)
			{
				throw new ArgumentException("Invalid drive-letter specified.", "driveLetter");
			}

			driveLetter = driveLetter.ToUpper();

			var driveConfiguration = _configuredDrives.FirstOrDefault(item => item.DriveLetter == driveLetter);

			if (driveConfiguration != null)
			{
				if (VirtualDriveService.Stop(driveConfiguration.DriveLetter))
				{
					_configuredDrives.Remove(driveConfiguration);

					SaveConfiguration();
				}
			}
		}

		public static void SetDriveConfiguration(string driveLetter, string volumeLabel, string connectionString)
		{
			if (Regex.IsMatch(driveLetter, "^[a-zA-Z]$") == false)
			{
				throw new ArgumentException("Invalid drive-letter specified.", "driveLetter");
			}

			if (volumeLabel != null && volumeLabel.Length > 32)
			{
				throw new ArgumentException("Invalid volumeLabel specified. VolumeLabel cannot exceed 32 characters in length.");
			}

			try
			{
				CloudStorageAccount.Parse(connectionString);
			}
			catch
			{
				throw new ArgumentException("Invalid connection string specified.", "connectionString");
			}

			driveLetter = driveLetter.ToUpper();

			var driveConfiguration = _configuredDrives.FirstOrDefault(item => item.DriveLetter == driveLetter);

			if (driveConfiguration == null)
			{
				driveConfiguration = new DriveConfiguration
				{
					DriveLetter = driveLetter,
					ConnectionString = connectionString,
					VolumeLabel = volumeLabel,
				};

				VirtualDriveService.Start(driveConfiguration.DriveLetter,
					driveConfiguration.VolumeLabel, driveConfiguration.ConnectionString);

				_configuredDrives.Add(driveConfiguration);
			}
			else
			{
				// If this drive is already running, check to see if we need to update the record.
				if (driveConfiguration.VolumeLabel != volumeLabel
					|| driveConfiguration.ConnectionString != connectionString)
				{
					driveConfiguration.ConnectionString = connectionString;
					driveConfiguration.VolumeLabel = volumeLabel;

					if (VirtualDriveService.Stop(driveConfiguration.DriveLetter))
					{
						VirtualDriveService.Start(driveConfiguration.DriveLetter, driveConfiguration.VolumeLabel, driveConfiguration.ConnectionString);
					}
				}
			}

			SaveConfiguration();
		}

		private static void SaveConfiguration()
		{
			var configDocument = new XDocument(new XElement("drives"));

			foreach (var driveConfiguration in _configuredDrives)
			{
				var driveElement = new XElement("drive");

				driveElement.Add(new XAttribute("driveLetter", driveConfiguration.DriveLetter));

				driveElement.Add(new XAttribute("connectionString", driveConfiguration.ConnectionString));

				driveElement.Add(new XAttribute("volumeLabel", driveConfiguration.VolumeLabel));

				configDocument.Root.Add(driveElement);
			}

			if (File.Exists(_configFilePath) == false)
			{
				string directory = Path.GetDirectoryName(_configFilePath);

				if (Directory.Exists(directory) == false)
				{
					Directory.CreateDirectory(directory);
				}
			}

			configDocument.Save(_configFilePath);
		}
	}

	public class DriveConfiguration
	{
		public string DriveLetter { get; set; }

		public string ConnectionString { get; set; }

		public string VolumeLabel { get; set; }
	}
}
